// Copyright 2016 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT

#pragma once

#include <lib/rodso.h>
#include <vm/vm_object.h>

class VmMapping;

class VDso : public RoDso {
public:
    // This is called only once, at boot time.
    static const VDso* Create();

    static bool vmo_is_vdso(const fbl::RefPtr<VmObject>& vmo) {
        return likely(instance_) && instance_->vmo_is_vdso_impl(vmo);
    }

    static bool valid_code_mapping(uint64_t vmo_offset, size_t size) {
        return instance_->RoDso::valid_code_mapping(vmo_offset, size);
    }

    // Given VmAspace::vdso_code_mapping_, return the vDSO base address or 0.
    static uintptr_t base_address(const fbl::RefPtr<VmMapping>& code_mapping);

    // Forward declaration of generated class.
    // This class is defined in the file vdso-valid-sysret.h,
    // which is generated by scripts/gen-vdso-valid-sysret.sh.
    // It has a static method named after each syscall:
    //     static bool <syscall-name>(uintptr_t offset);
    // This tests whether <start of vDSO code>+offset is a valid PC
    // for entering the kernel with <syscall-name>'s syscall number.
    struct ValidSyscallPC;

    enum class Variant {
        FULL,
        TEST1,
        TEST2,
        COUNT
    };

    static constexpr size_t variants() {
        return static_cast<size_t>(Variant::COUNT);
    }

    // Return a handle to the VMO for the given variant.
    HandleOwner vmo_handle(Variant) const;

private:
    VDso();
    void CreateVariant(Variant);

    bool vmo_is_vdso_impl(const fbl::RefPtr<VmObject>& vmo_ref) const {
        if (vmo_ref == vmo()->vmo())
            return true;
        for (const auto& v : variant_vmo_) {
            if (vmo_ref == v->vmo())
                return true;
        }
        return false;
    }

    static constexpr size_t variant_index(Variant v) {
        DEBUG_ASSERT(v > Variant::FULL);
        return static_cast<size_t>(v) - 1;
    }

    fbl::RefPtr<VmObjectDispatcher> variant_vmo_[
        static_cast<size_t>(Variant::COUNT) - 1];

    static const VDso* instance_;
};
